﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

using HumanWorkflow.SemanticModel;
using HumanWorkflow.LexicalModel;

using HumanWorkflow.TextualDslParser;
using Irony.Parsing;

namespace HumanWorkflow.TextualDslParser.IronyParser
{
    public class HwdlIronyModelBuilder
    {
        HwdlGrammar grammar;
        Parser parser;

        ParseTree rootNode;
        LexicalModelContext lexicalModel; 

        public HwdlIronyModelBuilder()
        {
            grammar = new HwdlGrammar();
            parser = new Parser(grammar);
        }

        public HumanWorkflowSemanticModel ParseHumanWorkflowFromFile(string filePathWithName)
        {
            TextReader tr = new StreamReader(filePathWithName);
            string hwText = tr.ReadToEnd();

            return ParseHumanWorkflow(hwText);
        }

        public HumanWorkflowSemanticModel ParseHumanWorkflow(string humanWorkflowDefinition)
        {
            rootNode = parser.Parse(humanWorkflowDefinition);

            //TODO: handle errors, return back parse error list
            string errorMessage = "";
            if (rootNode.HasErrors())
            {
                errorMessage = "Parsing failed with error: " + rootNode.ParserMessages.First<ParserMessage>().Message;
                throw new Exception(errorMessage);
            }

            lexicalModel = Transform_ParseTree2LexicalModel(rootNode);
            HumanWorkflowSemanticModel semanticModel = lexicalModel.TransformToSemanticModel();

            return semanticModel;
        }

        #region ParseTree 2 LexicalModel transformation

        LexicalModelContext Transform_ParseTree2LexicalModel(ParseTree parseTree)
        {
            lexicalModel = new LexicalModelContext();
            DepthFirstTraverseAST(parseTree.Root);
            NewStepToken(null);
            return lexicalModel;
        }

        void DepthFirstTraverseAST(ParseTreeNode parseTreeNode)
        {
            TransformNode(parseTreeNode);

            foreach (ParseTreeNode treeNode in parseTreeNode.ChildNodes)
            {
                DepthFirstTraverseAST(treeNode);
            }
        }

        public void TransformNode(ParseTreeNode treeNode)
        {
            switch (treeNode.Term.Name)
            {
                case GrammarConstants.it_workflow_def:
                    lexicalModel.WorkflowToken.Name = treeNode.Token.Text;
                    break;
                case GrammarConstants.BASE_STEP_TYPE:
                    NewStepToken(treeNode.FirstChild.Token.Text);
                    break;
                case GrammarConstants.MANUAL_STEP_TYPE:
                    NewStepToken(treeNode.FirstChild.Token.Text);
                    break;
                case GrammarConstants.kt_final:
                    NewStepToken(GrammarConstants.kt_final);
                    break;

                case GrammarConstants.it_base_step_def:
                    currentStepToken.Name = treeNode.Token.Text;
                    break;
                case GrammarConstants.it_manual_step_def:
                    currentStepToken.Name = treeNode.Token.Text;
                    break;

                case GrammarConstants.it_final_step_def:
                    currentStepToken.Name = treeNode.Token.Text;
                    break;

                case GrammarConstants.it_done_by_role_ref:
                    currentStepToken.Role = treeNode.Token.Text;
                    break;

                case GrammarConstants.TRANSITION:
                    NewTransitionToken(new TransitionToken());
                    break;
                case GrammarConstants.it_transition_def:
                    currentTransitionToken.Name = treeNode.Token.Text;
                    break;
                case GrammarConstants.it_transition_target_step_ref:
                    currentTransitionToken.ToStepName = treeNode.Token.Text;
                    break;

                case GrammarConstants.definitionComment:
                    currentDefinition = treeNode.Token.Text;
                    break;
            }
        }

        #endregion

        #region Context variables


        StepToken currentStepToken;
        TransitionToken currentTransitionToken;
        string currentDefinition = null;

        void NewStepToken(string stepTypeName)
        {
            StepToken newStepToken;
            if (String.IsNullOrEmpty(stepTypeName))
            {
                newStepToken = null;
            }
            else
            {
                newStepToken = new StepToken();

                switch (stepTypeName)
                {
                    case GrammarConstants.kt_initial:
                        newStepToken.StepType = StepTypeEnum.Initial;
                        break;

                    case GrammarConstants.kt_decision:
                        newStepToken.StepType = StepTypeEnum.Decision;
                        break;

                    case GrammarConstants.kt_automatic:
                        newStepToken.StepType = StepTypeEnum.SystemCommand;
                        break;

                    case GrammarConstants.kt_manual:
                        newStepToken.StepType = StepTypeEnum.HumanWork;
                        break;

                    case GrammarConstants.kt_final:
                        newStepToken.StepType = StepTypeEnum.Final;
                        break;
                }

                if (!String.IsNullOrEmpty(currentDefinition))
                {
                    newStepToken.Definition = currentDefinition;
                    currentDefinition = null;
                }
            }

            NewTransitionToken(null);

            if (currentStepToken != null)
            {
                lexicalModel.StepTokens.Add(currentStepToken);
            }

            currentStepToken = newStepToken;
        }

        void NewTransitionToken(TransitionToken newTransitionToken)
        {
            if (currentTransitionToken != null)
            {
                if (!String.IsNullOrEmpty(currentDefinition))
                {
                    currentTransitionToken.Definition = currentDefinition;
                    currentDefinition = null;
                }

                currentStepToken.OutTransitions.Add(currentTransitionToken);
            }

            currentTransitionToken = newTransitionToken;
        }

        #endregion

       
    }
}
